package com.google.code.solrdimension.config;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.google.code.solrdimension.config.converters.DimensionValueConverter;
import com.google.code.solrdimension.config.dimension.DimensionConfig;
import com.google.code.solrdimension.parsers.beans.AlphaRangeDimensionValue;
import com.google.code.solrdimension.parsers.beans.Dimension;
import com.google.code.solrdimension.parsers.beans.ExactDimensionValue;
import com.google.code.solrdimension.parsers.beans.IntRangeDimensionValue;
import com.google.code.solrdimension.parsers.beans.RegexDimensionValue;
import com.google.code.solrdimension.parsers.beans.SiftDimensionValue;
import com.google.code.solrdimension.parsers.beans.Solrdimension;
import com.google.code.solrdimension.parsers.exception.ParserBeanValidationException;
import com.google.code.solrdimension.parsers.mappers.JAXBXmlMapper;
import com.google.code.solrdimension.parsers.validators.ParserBeansValidator;
import com.google.code.solrdimension.spring.ApplicationConfig;

/**
 * 
 * The SolrDimensionXmlConfigurationService class is for retrieving the
 * configuration XML file, parse it with {@link JAXBXmlMapper} and then invoke
 * {@link ParserBeansValidator} to validate the configuration
 * 
 * 
 * @author Michael Chan
 * 
 */
public class XmlConfigurationServiceImpl implements XmlConfigurationService {

    private static Logger logger = LoggerFactory.getLogger(XmlConfigurationServiceImpl.class);
    
    private Map<String, ? extends DimensionValueConverter> dimVal2ConfigConverterMap;
    
    private ApplicationConfig applicationConfig;
    
    
    private DimensionConfigWrapper solrDimensionConfig = null;

    @Autowired
    @Qualifier("jaxbObjectXMLmapper")
    JAXBXmlMapper xmlMapper;

    
    public DimensionConfigWrapper getSolrDimensionConfig(){
        
        if (solrDimensionConfig == null){
            
            try {
                solrDimensionConfig = this.getSolrDimensionXmlConfiguration();
            } catch (ParserBeanValidationException e){
                logger.error("Could not create SolrDimensionConfig, please check XML configuration file");
                e.printStackTrace();
            }
            
        }
        
        return solrDimensionConfig;
    }
    
    /**
     * 
     * Retrieves the parsed XML config, and validates the bean.
     * 
     * 
     * 
     * @return the XML configuration encapsulated in an instance of
     *         {@link Solrdimension}
     */
    private DimensionConfigWrapper getSolrDimensionXmlConfiguration() throws ParserBeanValidationException {
        
        DimensionConfigWrapper mainconfig = null;
        try {

            logger.info("Parsing Solr Dimension configuration File...");

            Solrdimension parsedConfig = (Solrdimension) (xmlMapper.readObjectFromXml(applicationConfig.getSolrDimensionXmlConfigFileAsInputStream()).getValue());

            logger.info("Validating configuration data...");
            if (ParserBeansValidator.ValidateDimension(parsedConfig)) {

                mainconfig = createSolrDimensionConfig(parsedConfig);

            } else {
                throw new ParserBeanValidationException("Validation error on configuration");
            }

        } catch (IOException e) {
            logger.error("Error reading SolrDimension config file");
            e.printStackTrace();

            return null;
        }

        return mainconfig;

    }

    /**
     * Create an instance of SolrDimensionConfig object from the objects created
     * from the XML config parser. This should only be called once during the
     * initialization of SolrDimension
     * 
     * @param parsedConfig
     * @return
     */
    private DimensionConfigWrapper createSolrDimensionConfig(Solrdimension parsedConfig) {

        DimensionConfigWrapper mainconfig = new DimensionConfigWrapper();

        List<DimensionConfig> dimensionConfigs = new ArrayList<DimensionConfig>();

        for (Dimension dimension : parsedConfig.getDimensions().getDimension()) {

            DimensionConfig config = new DimensionConfig();

            mainconfig.addSolrFieldToModify(dimension.getSolrFieldName());

            config.setDisplayName(dimension.getName());
            
            if (DimensionConfig.TYPE_DEFAULT.equals(dimension.getType())){
                config.setId(dimension.getSolrFieldName());
            } else {
                config.setId(dimension.getId());
            }
            
            
            
            config.setDependOn(dimension.getDependOn());
            config.setType(dimension.getType());
            config.setSolrFieldName(dimension.getSolrFieldName());
            config.setValueOrder(dimension.getValueOrder());
            

            if (dimension.getExactDimensionValue() != null && !dimension.getExactDimensionValue().isEmpty()) {

                config.setDimensionValueConfigs(getConfigCoverter(ExactDimensionValue.class.getName()).convert(dimension.getExactDimensionValue(), dimension));

            } else if (dimension.getAlphaRangeDimensionValue() != null && !dimension.getAlphaRangeDimensionValue().isEmpty()) {

                config.setDimensionValueConfigs(getConfigCoverter(AlphaRangeDimensionValue.class.getName()).convert(dimension.getAlphaRangeDimensionValue(), dimension));


            } else if (dimension.getRegexDimensionValue() != null && !dimension.getRegexDimensionValue().isEmpty()) {

                config.setDimensionValueConfigs(getConfigCoverter(RegexDimensionValue.class.getName()).convert(dimension.getRegexDimensionValue(), dimension));


            } else if (dimension.getSiftDimensionValue() != null && !dimension.getSiftDimensionValue().isEmpty()) {

                config.setDimensionValueConfigs(getConfigCoverter(SiftDimensionValue.class.getName()).convert(dimension.getSiftDimensionValue(), dimension));


            } else if (dimension.getIntRangeDimensionValue() != null && !dimension.getIntRangeDimensionValue().isEmpty()) {

                config.setDimensionValueConfigs(getConfigCoverter(IntRangeDimensionValue.class.getName()).convert(dimension.getIntRangeDimensionValue(), dimension));


            }
            
            
            dimensionConfigs.add(config);
        }

        mainconfig.setDimensionConfigs(dimensionConfigs);

        return mainconfig;

    }



    

    /**
     * 
     * @param fullyQualifyDimensionValueClassName
     * @return - an instance of DimensionValueConverter
     */
    public DimensionValueConverter getConfigCoverter(String fullyQualifyDimensionValueClassName) {

        return dimVal2ConfigConverterMap.get(fullyQualifyDimensionValueClassName);

    }

    public void setDimVal2ConfigConverterMap(Map<String, ? extends DimensionValueConverter> map) {
        this.dimVal2ConfigConverterMap = map;
    }
    
    
    public void setApplicationConfig (ApplicationConfig config){
        this.applicationConfig = config;
    }

}
